#ifndef SRC_TYPE_H
#define SRC_TYPE_H

#include <memory>
#include <list>
#include "lexer.h"
#include "AstNode.h"
#include "Visitor.h"

class BuildInType;
class PointerType;
class FunctionType;
class ArrayType;

class Type {
public:
    enum class TypeClass { 
        BuiltInType,
        PointerType,
        FunctionType,
        ArrayType,
    };
    int Size;
    int Align;

    static std::shared_ptr<BuildInType> CharType;
    static std::shared_ptr<BuildInType> ShortType;
    static std::shared_ptr<BuildInType> IntType;
    static std::shared_ptr<BuildInType> LongType;

private:
    TypeClass TC;
public:
    virtual ~Type() {}
    Type(TypeClass tc, int size, int align):TC(tc), Size(size), Align(align) {}

    bool IsIntegerType() const;
    bool IsPointerType() const;
    bool IsFunctionType() const;
    bool IsArrayType() const;
};


class BuildInType: public Type {
public:
    enum class Kind { 
        Char,
        Short,
        Int,
        Long,
    };
private:
    Kind Kd;
public:
    BuildInType(Kind kd, int size, int align): Type(TypeClass::BuiltInType, size, align), Kd(kd) {}

    Kind GetKind() const {
        return Kd;
    }
};


class PointerType: public Type {
private:
    
public:
    std::shared_ptr<Type> Base;
    PointerType(std::shared_ptr<Type> base): Type(TypeClass::PointerType, 8, 8), Base(base) {}
};


struct Param {
    std::shared_ptr<Type> Ty;
    std::shared_ptr<Token> tk;
};

class FunctionType: public Type {
private:
    std::shared_ptr<Type> ReturnType;
public:
    std::list<std::shared_ptr<Param>> Params;
    FunctionType(std::shared_ptr<Type> returnType): Type(TypeClass::FunctionType, 8, 8), ReturnType(returnType) {}
};


class ArrayType : public Type {
public:
    std::shared_ptr<Type> elementType;
    int ArrayLen;
    ArrayType(std::shared_ptr<Type> elemType, int len) 
    : Type(TypeClass::ArrayType, len * elemType->Size, elemType->Align), elementType(elemType), ArrayLen(len) {}
};


class TypeVisitor : public AstVisitor {
public:

    static TypeVisitor* Visitor() {
        static TypeVisitor MVisitor;
        return &MVisitor;
    }

    void VisitProgramNode(ProgramAstNode *node) override;
    void VisitFunctionNode(FunctionNode *node) override;
    void VisitExprStatmNode(ExprStatmNode *node) override;
    void VisitFuncCallNode(FuncCallNode *node) override;
    void VisitDeclarationStatmNode(DeclarationStatmNode *node) override;
    void VisitIfStatmNode(IfStatmNode *node) override;
    void VisitWhileStatmNode(WhileStatmNode *node) override;
    void VisitForStatmNode(ForStatmNode *node) override;
    void VisitBlockStatmNode(BlockStatmNode *node) override;
    void VisitReturnStatmNode(ReturnStatmNode *node) override;
    void VisitAssignExprNode(AssignExprNode *node) override;
    void VisitBinaryNode(BinaryAstNode *node) override;
    void VisitUnaryNode(UnaryAstNode *node) override;
    void VisitConstantNode(ConstantAstNode *node) override;
    void VisitSizeofNode(SizeofNode *node) override;
    void VisitVarNode(VarAstNode *node) override;
};


#endif // !SRC_TYPE_H